package clw.toy;

import java.util.List;

/**
 * @author chengliwei
 */
public abstract class Expr {

    interface Visitor<R> {
        R visitBinary(Binary binary);

        R visitGrouping(Grouping grouping);

        R visitLiteral(Literal literal);

        R visitUnary(Unary unary);

        R visitVariableExpr(Variable variable);

        R visitAssignExpr(Assign assign);

        R visitThisExpr(This thisExpr);

        R visitLogicalExpr(Logical logical);

        R visitCallExpr(Call call);

        R visitGetExpr(Get get);

        R visitSetExpr(Set set);

        R visitSuperExpr(Super superExpr);

        R visitSelfIncrementOrDecrement(SelfIncrementOrDecrement expr);
    }

    abstract <R> R accept(Visitor<R> visitor);

    static class SelfIncrementOrDecrement extends Expr {

        Expr left;
        Token operator;

        SelfIncrementOrDecrement(Expr left, Token operator) {
            this.left = left;
            this.operator = operator;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitSelfIncrementOrDecrement(this);
        }
    }

    static class Binary extends Expr {

        Expr left;
        Token operator;
        Expr right;

        Binary(Expr left, Token operator, Expr right) {
            this.left = left;
            this.operator = operator;
            this.right = right;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitBinary(this);
        }
    }

    static class Grouping extends Expr {

        Expr expression;

        Grouping(Expr expression) {
            this.expression = expression;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitGrouping(this);
        }
    }

    static class Literal extends Expr {

        Object value;

        Literal(Object value) {
            this.value = value;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitLiteral(this);
        }
    }

    static class Unary extends Expr {
        Token operator;
        Expr right;

        Unary(Token operator, Expr right) {
            this.operator = operator;
            this.right = right;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitUnary(this);
        }
    }

    static class Variable extends Expr {

        Token name;

        public Variable(Token name) {
            this.name = name;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitVariableExpr(this);
        }
    }

    static class Assign extends Expr {
        Token name;
        Expr value;

        public Assign(Token name, Expr value) {
            this.name = name;
            this.value = value;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitAssignExpr(this);
        }
    }

    static class This extends Expr {

        Token keyword;

        public This(Token keyword) {
            this.keyword = keyword;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitThisExpr(this);
        }
    }

    static class Logical extends Expr {
        Expr left;
        Token operator;
        Expr right;

        public Logical(Expr left, Token operator, Expr right) {
            this.left = left;
            this.operator = operator;
            this.right = right;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitLogicalExpr(this);
        }
    }

    static class Set extends Expr {
        Expr object;
        Token name;
        Expr value;

        public Set(Expr object, Token name, Expr value) {
            this.object = object;
            this.name = name;
            this.value = value;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitSetExpr(this);
        }
    }

    static class Get extends Expr {
        Expr object;
        Token name;

        public Get(Expr object, Token name) {
            this.object = object;
            this.name = name;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitGetExpr(this);
        }
    }

    static class Super extends Expr {
        Token keyword;
        Token method;

        public Super(Token keyword, Token method) {
            this.keyword = keyword;
            this.method = method;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitSuperExpr(this);
        }
    }

    static class Call extends Expr {
        Expr callee;
        Token paren;
        List<Expr> arguments;

        public Call(Expr callee, Token paren, List<Expr> arguments) {
            this.callee = callee;
            this.paren = paren;
            this.arguments = arguments;
        }

        @Override
        <R> R accept(Visitor<R> visitor) {
            return visitor.visitCallExpr(this);
        }
    }
}
